1
/****************************** Module Header ******************************\
2 * Module Name: SqlAzureHelper.cs
3 * Project: CSSqlAzurePartitioning
4 * Copyright (c) Microsoft Corporation.
6 * This file is from Microsoft SQL Azure team's blog.
7 * http://blogs.msdn.com/b/sqlazure/archive/2010/05/17/10014011.aspx
9 * 1. Implements forward read only cursors for performance.
10 * 2. Support IEnumerable and LINQ
11 * 3. Disposes of the connection and the data reader when the result set is no longer needed.
14 * This source is subject to the Microsoft Public License.
15 * See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
16 * All other rights reserved.
18 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
19 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
21 \***************************************************************************/
24 using System
.Collections
.Generic
;
26 using System
.Data
.SqlClient
;
27 using System
.Data
.Common
;
28 using System
.Configuration
;
29 using System
.Threading
;
33 internal delegate SqlDataReader
ExecuteReaderDelegate(SqlConnection sqlConnection
);
34 internal delegate void ExecuteNonQueryDelegate(SqlConnection sqlConnection
);
36 sealed class SQLAzureHelper
38 internal static IEnumerable
<DbDataRecord
> ExecuteReader(String connectionString
, ExecuteReaderDelegate executeDelegate
)
40 using (SqlConnection sqlConnection
= new SqlConnection(connectionString
))
42 // Open the connection
45 using (SqlDataReader sqlDataReader
= executeDelegate(sqlConnection
))
47 foreach (DbDataRecord dbDataRecord
in sqlDataReader
.Cast
<DbDataRecord
>())
48 yield return dbDataRecord
;
53 internal static void ExecuteNonQuery(String connectionString
, ExecuteNonQueryDelegate executeDelegate
)
55 // This is the retry loop, handling the retries session
56 // is done in the catch for performance reasons
57 for (Int32 attempt
= 1; ; )
61 using (SqlConnection sqlConnection
= new SqlConnection(connectionString
))
63 // Open the connection
65 executeDelegate(sqlConnection
);
67 // Success Break Out Of Attempt Loop
70 catch (SqlException sqlException
)
76 Int32 maxRetryCount
= Int32
.Parse(
77 ConfigurationManager
.AppSettings
["ConnectionRetrys"]);
79 // Throw Error if we have reach the maximum number of retries
80 if (attempt
== maxRetryCount
)
83 // Determine if we should retry or abort.
84 if (!SQLAzureHelper
.RetryLitmus(sqlException
))
87 Thread
.Sleep(SQLAzureHelper
.ConnectionRetryWaitSeconds(attempt
));
93 /// Number of seconds to wait before retrying the connection
95 /// <param name="attempt"></param>
96 /// <returns></returns>
97 public static Int32
ConnectionRetryWaitSeconds(Int32 attempt
)
99 Int32 connectionRetryWaitSeconds
= Int32
.Parse(ConfigurationManager
.
100 AppSettings
["ConnectionRetryWaitSeconds"])
103 // Backoff Throttling, here we slow the retries, based on the Number of
105 connectionRetryWaitSeconds
= connectionRetryWaitSeconds
*
106 (Int32
)Math
.Pow(2, attempt
);
108 return (connectionRetryWaitSeconds
);
112 /// Determine from the exception if the execution
113 /// of the connection should Be attempted again
115 /// <param name="exception">Generic Exception</param>
116 /// <returns>True if a a retry is needed, false if not</returns>
117 public static Boolean
RetryLitmus(SqlException sqlException
)
119 switch (sqlException
.Number
)
121 // The service has encountered an error
122 // processing your request. Please try again.
125 // The service is currently busy. Retry
126 // the request after 10 seconds. Code: %d.
128 //A transport-level error has occurred when
129 // receiving results from the server. (provider:
130 // TCP Provider, error: 0 - An established connection
131 // was aborted by the software in your host machine.)
140 /// Names of the Databases In Horizontal Partition
142 public static String
[] ConnectionStringNames
= { "Database001ConnectionString", "Database002ConnectionString" }
;
145 /// Connections Strings In the Horizontal Partition
147 /// <returns></returns>
148 public static IEnumerable
<String
> ConnectionStrings()
150 foreach (String connectionStringName
in ConnectionStringNames
)
151 yield return ConfigurationManager
.ConnectionStrings
[connectionStringName
].ConnectionString
;
155 /// Return the Index to the Database For the Primary Key
157 /// <param name="primaryKey"></param>
158 /// <returns></returns>
159 private static int DatabaseIndex(Guid primaryKey
)
163 foreach (byte b
in primaryKey
.ToByteArray())
166 hash
+= (hash
<< 10);
172 hash ^
= (hash
>> 11);
173 hash
+= (hash
<< 15);
175 return ((int)(hash
% 100000000));
179 /// Returns the Connection String Name for the Primary Key
181 /// <param name="primaryKey"></param>
182 /// <returns></returns>
183 private static String
ConnectionStringName(Guid primaryKey
)
185 return (ConnectionStringNames
[DatabaseIndex(primaryKey
) % ConnectionStringNames
.Length
]);
189 /// Returns the Connection String For the Primary Key
191 /// <param name="primaryKey"></param>
192 /// <returns></returns>
193 public static String
ConnectionString(Guid primaryKey
)
195 return (ConfigurationManager
.ConnectionStrings
[ConnectionStringName(primaryKey
)].ConnectionString
);